#pragma once
#include <Rz/Types.h>
#include <Rz/Graphics/Render/Libraries/OpenGL/API/GLDefinitions.h>
#include <Rz/Graphics/Render/Libraries/OpenGL/API/GLExt.h>
#include <Rz/Graphics/Render/Libraries/OpenGL/API/GLWinExt.h>

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>

namespace Rz { namespace Graphics { namespace Render { namespace Libraries { namespace OpenGL {

struct GLState
{
	GLenum	ActiveTexture;
	GLenum  CullFace;
	GLenum	DepthFunc;
	GLenum	BlendFuncSrcFactor;
	GLenum	BlendFuncDestFactor;
	GLenum	BlendEquation;
	GLint   ViewPortX;
	GLint   ViewPortY;
	GLsizei ViewPortWidth;
	GLsizei ViewPortHeight;
	GLenum  PolygonFace;
	GLenum	PolygonMode;

	GLuint  LastBuffer;
	GLuint  LastVertexArray;
	GLuint  LastTexture;
	GLuint  LastProgramUsed;
};

class GL
{
	public:
		static bool Init();

		// GL Arrays
		static GLuint GenBuffer();
		static void BindBuffer(GLenum target, GLuint buffer);
		static void BufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
		static void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
		static void DeleteBuffer(GLuint buffer);

		// GL Shaders
		static GLuint CreateShader(GLenum type);
		static void ShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
		static void CompileShader(GLuint shader);
		static GLboolean IsShader(GLuint shader);
		static void GetShaderInfoLog(GLuint shader, GLsizei buffSize, GLsizei* length, GLchar* infoLog);
		static GLuint CreateProgram();
		static void AttachShader(GLuint program, GLuint shader);
		static void LinkProgram(GLuint program);
		static void UseProgram(GLuint program);
		static void GetProgramInfoLog(GLuint program, GLsizei buffSize, GLsizei* length, GLchar* infoLog);
		static GLint GetAttribLocation(GLuint program, const GLchar* name);
		static GLint GetUniformLocation(GLuint program, const GLchar* name);
		static void DeleteShader(GLuint shader);
		static void DeleteProgram(GLuint program);
		static void Uniform1f(GLint location, GLfloat value);
		static void Uniform1i(GLint location, GLint value);
		static void Uniform2fv(GLint location, const GLfloat* value);
		static void Uniform2iv(GLint location, const GLint* value);
		static void Uniform3fv(GLint location, const GLfloat* value);
		static void Uniform3iv(GLint location, const GLint* value);
		static void Uniform4fv(GLint location, const GLfloat* value);
		static void Uniform4iv(GLint location, const GLint* value);
		static void UniformMatrix3f(GLint location, const GLfloat* value);
		static void UniformMatrix4f(GLint location, const GLfloat* value);

		// VBO
		static GLuint GenVertexArray();
		static void BindVertexArray(GLuint array);
		static void DeleteVertexArray(GLuint array);
		static void VertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer);
		static void EnableVertexAttribArray(GLuint index);
		static void DrawArrays(GLenum mode, GLint first, GLsizei count);
		static void DrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);

		// FBO
		static GLuint GenFramebuffer();
		static void BindFramebuffer(GLuint framebuffer);
		static void DeleteFramebuffer(GLuint framebuffer);
		static GLenum CheckFramebufferStatus(GLenum target);
		static void FramebufferTexture(GLenum attachment, GLuint texture, GLint level);
		static void FramebufferTexture1D(GLenum attachment, GLenum textarget, GLuint texture, GLint level);
		static void FramebufferTexture2D(GLenum attachment, GLenum textarget, GLuint texture, GLint level);
		static void FramebufferTexture3D(GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
		static void FramebufferRenderbuffer(GLenum attachment, GLuint renderbuffer);
	
		// Renderbuffer
		static GLuint GenRenderbuffer();
		static void BindRenderbuffer(GLuint renderbuffer);
		static void DeleteRenderbuffer(GLuint renderbuffer);
		static void RenderbufferStorage(GLenum internalformat, GLsizei width, GLsizei height);

		// Textures
		static GLuint GenTexture();
		static void BindTexture(GLenum target, GLuint texture);
		static void DeleteTexture(GLuint texture);
		static void ActiveTexture(GLenum texture);
		static void TexImage1D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid* pixels);
		static void TexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,GLenum format, GLenum type, const GLvoid* pixels);
		static void TexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels);
		static void TexParameterf(GLenum target, GLenum pname, GLfloat param);
		static void TexParameteri(GLenum target, GLenum pname, GLint param);
		static void GenerateMipmap(GLenum target);

		// Samplers
		static GLuint GenSampler();
		static void BindSampler(GLenum target, GLuint sampler);
		static void DeleteSampler(GLuint sampler);
		static void SamplerParameter(GLuint sampler, GLenum pname, GLint param);

		// VSync
		static bool SwapInterval(u32 interval);
		static int  GetSwapInterval();

		// MRT
		static void DrawBuffer(GLenum mode);
		static void DrawBuffers(GLsizei n, const GLenum* bufs);

		// Main
		static void Enable(GLenum cap);
		static void Disable(GLenum cap);
		static void CullFace(GLenum mode);
		static void FrontFace(GLenum mode);
		static void Clear(GLbitfield mask);
		static void ClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
		static void ClearDepth(GLclampf depth);
		static void DepthMask(GLboolean flag);
		static void DepthFunc(GLenum func);
		static void StencilMask(GLuint mask);
		static void StencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
		static void StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
		static void StencilFunc(GLenum func, GLint ref, GLuint mask);
		static void StencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask);
		static void BlendFunc(GLenum sfactor, GLenum dfactor);
		static void BlendEquation(GLenum mode);
		static void PointSize(GLfloat size);
		static void PolygonMode(GLenum face, GLenum mode);
		static void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);

		// Own
		static bool PushState();
		static bool PopState();

	private:
		// GL Arrays
		static PFNGLGENBUFFERSPROC				_genBuffers;
		static PFNGLBINDBUFFERPROC				_bindBuffer;
		static PFNGLBUFFERDATAPROC				_bufferData;
		static PFNGLBUFFERSUBDATAPROC			_bufferSubData;
		static PFNGLDELETEBUFFERSPROC			_deleteBuffers;

		// GL Shaders
		static PFNGLCREATESHADERPROC			_createShader;
		static PFNGLSHADERSOURCEPROC			_shaderSource;
		static PFNGLCOMPILESHADERPROC			_compileShader;
		static PFNGLISSHADERPROC				_isShader;
		static PFNGLGETSHADERINFOLOGPROC		_getShaderInfoLog;
		static PFNGLGETPROGRAMINFOLOGPROC		_getProgramInfoLog;
		static PFNGLCREATEPROGRAMPROC			_createProgram;
		static PFNGLATTACHSHADERPROC			_attachShader;
		static PFNGLLINKPROGRAMPROC				_linkProgram;
		static PFNGLUSEPROGRAMPROC				_useProgram;
		static PFNGLGETATTRIBLOCATIONPROC		_getAttribLocation;
		static PFNGLGETUNIFORMLOCATIONPROC		_getUniformLocation;
		static PFNGLDELETESHADERPROC			_deleteShader;
		static PFNGLDELETEPROGRAMPROC			_deleteProgram;
		static PFNGLUNIFORM1FPROC				_uniform1f;
		static PFNGLUNIFORM1IPROC				_uniform1i;
		static PFNGLUNIFORM2FVPROC				_uniform2fv;
		static PFNGLUNIFORM2IVPROC				_uniform2iv;
		static PFNGLUNIFORM3FVPROC				_uniform3fv;
		static PFNGLUNIFORM3IVPROC				_uniform3iv;
		static PFNGLUNIFORM4FVPROC				_uniform4fv;
		static PFNGLUNIFORM4IVPROC				_uniform4iv;
		static PFNGLUNIFORMMATRIX3FVPROC		_uniformMatrix3f;
		static PFNGLUNIFORMMATRIX4FVPROC		_uniformMatrix4f;

		// VBO
		static PFNGLGENVERTEXARRAYSPROC			_genVertexArrays;
		static PFNGLBINDVERTEXARRAYPROC			_bindVertexArray;
		static PFNGLDELETEVERTEXARRAYSPROC		_deleteVertexArrays;
		static PFNGLVERTEXATTRIBPOINTERPROC		_vertexAttribPointer;
		static PFNGLENABLEVERTEXATTRIBARRAYPROC _enableVertexAttribArray;
		static PFNGLDRAWARRAYSPROC				_drawArrays;
		static PFNGLDRAWELEMENTSPROC			_drawElements;

		// FBO
		static PFNGLGENFRAMEBUFFERSPROC			_genFramebuffers;
		static PFNGLBINDFRAMEBUFFERPROC			_bindFramebuffer;
		static PFNGLDELETEFRAMEBUFFERSPROC		_deleteFramebuffers;
		static PFNGLCHECKFRAMEBUFFERSTATUSPROC	_checkFramebufferStatus;
		static PFNGLFRAMEBUFFERTEXTUREPROC		_framebufferTexture;
		static PFNGLFRAMEBUFFERTEXTURE1DPROC	_framebufferTexture1D;
		static PFNGLFRAMEBUFFERTEXTURE2DPROC	_framebufferTexture2D;
		static PFNGLFRAMEBUFFERTEXTURE3DPROC	_framebufferTexture3D;
		static PFNGLFRAMEBUFFERRENDERBUFFERPROC	_framebufferRenderbuffer;

		// Renderbuffer
		static PFNGLGENRENDERBUFFERSPROC		_genRenderbuffers;
		static PFNGLDELETERENDERBUFFERSPROC		_deleteRenderbuffers;
		static PFNGLBINDRENDERBUFFERPROC		_bindRenderbuffer;
		static PFNGLRENDERBUFFERSTORAGEPROC		_renderbufferStorage;

		// Textures
		static PFNGLACTIVETEXTUREPROC			_activeTexture;
		static PFNGLTEXIMAGE3DPROC				_texImage3D;
		static PFNGLGENERATEMIPMAPPROC			_generateMipmap;

		// Samplers
		static PFNGLGENSAMPLERSPROC				_genSamplers;
		static PFNGLDELETESAMPLERSPROC			_deleteSamplers;
		static PFNGLBINDSAMPLERPROC				_bindSampler;
		static PFNGLSAMPLERPARAMETERIPROC		_samplerParameter;

		// VSync
		static PFNWGLSWAPINTERVALEXTPROC		_swapInterval;
		static PFNWGLGETSWAPINTERVALEXTPROC		_getSwapInterval;

		// MRT
		static PFNGLDRAWBUFFERPROC				_drawBuffer;
		static PFNGLDRAWBUFFERSPROC				_drawBuffers;

		// GL Main & Deprecated
		static PFNGLBLENDEQUATIONPROC			_blendEquation;
		static PFNGLSTENCILOPSEPARATEPROC		_stencilOpSeparate;
		static PFNGLSTENCILFUNCSEPARATEPROC		_stencilFuncSeparate;

		static GLState _GLState;
		static GLState _GLStateStack[16];
		static u8	   _GLStateStackPointer;
};

} } } } }
